package com.sea.base.vm

import androidx.annotation.MainThread
import com.google.gson.JsonSyntaxException
import com.sea.base.bean.BaseResp
import com.sea.base.bean.BaseTypeResp
import com.sea.base.bean.PageResp
import com.sea.base.ext.global.ifDebug
import com.sea.base.ext.global.isAvailableNetwork
import com.sea.base.ext.global.isDebug
import com.sea.base.ext.global.log
import com.sea.base.ext.global.logE
import com.sea.base.ext.global.postMainSmart
import com.sea.base.ext.global.throwIfDebug
import com.sea.base.ext.global.toast
import com.sea.base.ext.global.toastAndLogDebug
import com.sea.base.ext.obs.smartPost
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.json.JSONException
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.util.concurrent.TimeoutException
import javax.net.ssl.SSLException

private const val TAG = "NetDataLog"

/**
 * 启动基于vm生命周期的协程，可监听接口状态[state]
 * @param tag 只是接口描述，没其他作用
 * @param state 接口加载状态回调，[createDefLoadingFun]、[BaseViewModel.createLiveDataLoadingFun]
 * @param block 主线程协程
 */
fun CoroutineScope.netLaunch(tag: String, @MainThread state: (NetStateData) -> Unit, @MainThread block: suspend CoroutineScope.() -> Unit) =
    netLaunch(tag, true, state, block)

/**
 * 启动基于vm生命周期的协程，可监听接口状态[state]
 * @param tag 只是接口描述，没其他作用
 * @param state 接口加载状态回调，[createDefLoadingFun]、[BaseViewModel.createLiveDataLoadingFun]
 * @param checkNetwork 是否校验网络（消息专用）
 * @param block 主线程协程
 */
fun CoroutineScope.netLaunch(tag: String, checkNetwork: Boolean, @MainThread state: (NetStateData) -> Unit, @MainThread block: suspend CoroutineScope.() -> Unit): Job {
    val exHandler = CoroutineExceptionHandler { ctx, throwable ->
        val name: String? = ctx[CoroutineName]?.name
        ifDebug {
            if (throwable is BaseNetException && throwable.netCode == BaseNetException.NET_EMPTY_CODE) {
                "ctxName:$name，空数据".log(TAG)
            } else {
                throwable.printStackTrace()
                "ctxName:$name，异常：$throwable".logE(TAG)
            }
        }
        postMainSmart {
            try {
                val info = transformException(throwable)
                if (info.netCode == BaseNetException.NET_EMPTY_CODE) {
                    state.invoke(NetStateData(NetState.EMPTY, tag, null))
                } else {
                    state.invoke(NetStateData(NetState.FAIL, tag, info))
                }
            } catch (e: Throwable) {
                throwIfDebug("接口回调多次崩溃", e)
            }
        }
    }

    return this.launch(Dispatchers.Main + exHandler + CoroutineName(tag)) {
        state.invoke(NetStateData(NetState.LOADING, tag, null))
        if (checkNetwork && !isAvailableNetwork()) {
            throw ConnectException("网络链接不可用")
        }
        block.invoke(this)
        state.invoke(NetStateData(NetState.SUCCESS, tag, null))
    }
}

fun createDefLoadingFun() = createDefLoadingFun { false }

/**
 * 默认的Loading效果，只增加了toast和登录失效
 */
inline fun createDefLoadingFun(crossinline intercept: (NetStateData) -> Boolean) =
    { nsd: NetStateData ->
        if (!intercept.invoke(nsd)) {
            //有异常的情况下默认toast
            nsd.info?.let { e ->
                if (isDebug) {
                    "${e.msg}，${nsd.tag}，${e.code}，${e.netCode}".toastAndLogDebug()
                } else {
                    e.msg.toast()
                }
                when {
                    e.code == BaseResp.LOGIN_OUT_CODE || e.netCode == BaseNetException.NET_LOGIN_OUT_CODE -> {
                        //登录失效
                        vmCallLoginOut.smartPost(Unit)
                    }
                    //其他默认判断
                }
            }
        }
    }


/**
 * 不需要data内容的数据校验
 */
@Suppress("RedundantSuspendModifier")
suspend inline fun BaseResp.checkBaseResp() {
    if (this.code != BaseResp.SUCCESS_CODE) {
        throw BaseNetException(code = this.code, msg = this.realMessage)
    }
}

/**
 * 普通数据校验
 * @param dataEmpty 数据是否为空，为空则抛出空数据异常
 */
suspend inline fun <T> BaseTypeResp<T>.checkBaseTypeResp(dataEmpty: (T) -> Boolean = { false }): T {
    this.checkBaseResp()
    if (this.data == null || dataEmpty.invoke(this.data)) {
        throw BaseNetException(0, BaseNetException.NET_EMPTY_CODE, "无数据")
    }
    return this.data
}

/**
 * list数据校验
 */
suspend inline fun <T> BaseTypeResp<List<T>>.checkBaseTypeRespList(): List<T> {
    return checkBaseTypeResp { it.isEmpty() }
}

/**
 * page list数据校验
 */
suspend inline fun <T> BaseTypeResp<PageResp<T>>.checkBaseTypeRespPageList(): List<T> {
    return checkBaseTypeResp { it.list.isEmpty() }.list
}

//待添加page的请求

private fun transformException(e: Throwable): BaseNetException {
    var netCode = BaseNetException.NET_EXCEPTION_CODE
    val st = when (e) {
        is BaseNetException -> return e
        is JSONException, is JsonSyntaxException -> "数据解析异常"

        is HttpException -> {
            netCode = e.code()
            "Http 异常 code:${e.code()} msg:${e.message()}"
        }

        is UnknownHostException -> "目标网址不存在"
        is SSLException -> "无法连接到目标网址"
        is ConnectException -> "网络链接异常"
        is SocketTimeoutException, is TimeoutException -> "网络连接超时"
        is SocketException -> "请求失败：${e.message}"
        else -> "未知异常：${e.message}"
    }
    return BaseNetException(0, netCode, st, e)
}